#include <sys_config.h>
#include <hld/net/net.h>
#include <hld/wifi/wifi_api.h>
#include <api/libnet/libnet.h>
#include <api/libtcpip/lwip/ip_addr.h>


typedef INT32 (*RT5370_INIT_CALLBACK)(UINT32);

INT32 (*rt5370_init_func)(UINT32);

ID	wifi_switch_mutex;

#ifdef WIFI_SUPPORT
/* Function Implement */
int WiFi_GetInfo(struct net_device *dev, UINT32 info_type, void *info_buf)
{
	struct net_device *net_dev;
	
	net_dev = (struct net_device *)dev_get_by_type(NULL, HLD_DEV_TYPE_WIFI);
	if(net_dev!=dev)
	{
		dev=net_dev;
	}

	if(dev == NULL)
		return -1;
	
	if(dev->get_info)
	{
		return dev->get_info(dev,info_type,info_buf);
	}
	else {
		WiFi_PRINTF("[ERR]%s: WiFi device plug out or not init done\n", __FUNCTION__);
		return -1;
	}
}

int WiFi_SetInfo(struct net_device *dev, UINT32 info_type, char*  arg)  //cpl_0312
{
	/*
		This API can set your own mac address for your dervice,
		Note the this API can only called before you calling "WiFi_SwtichMedia" 
		In other words, the mac address should be set before initializing the wifi device , or the initial sequence will fail
	*/

	struct net_device *net_dev;
	INT	is, mac_len;
	
	net_dev = (struct net_device *)dev_get_by_type(NULL, HLD_DEV_TYPE_WIFI);
	if(net_dev!=dev)
	{
		dev=net_dev;
	}

	if(dev == NULL)
		return -1;

	if(dev->set_info)
	{
		return dev->set_info(dev,info_type,arg);
	}
	else {
		WiFi_PRINTF("[ERR]%s: WiFi device plug out or not init done\n", __FUNCTION__);
		return -1;
	}

	
}


int WiFi_ScanAP(struct net_device *dev)
{
	struct net_device *net_dev;
	
	net_dev = (struct net_device *)dev_get_by_type(NULL, HLD_DEV_TYPE_WIFI);
	if(net_dev!=dev)
	{
		dev=net_dev;
	}

	if(dev == NULL)
		return -1;
	
	if(dev->scan_ap)
	{
		return dev->scan_ap(dev);
	}
	else {
		WiFi_PRINTF("[ERR]%s: WiFi device plug out or not init done\n", __FUNCTION__);
		return -1;
	}
}

int WiFi_GetAPList(struct net_device *dev, struct wifi_ap_info ap_list [ ], int numAP)
{
	struct net_device *net_dev;

	net_dev = (struct net_device *)dev_get_by_type(NULL, HLD_DEV_TYPE_WIFI);
	if(net_dev!=dev)
	{
		dev=net_dev;
	}
	if(dev == NULL)
		return -1;
	
	if(dev->get_ap_list)
	{
		return dev->get_ap_list(dev, (UINT32)ap_list, numAP);
	}
	else {
		WiFi_PRINTF("[ERR]%s: WiFi device plug out or not init done\n", __FUNCTION__);
		return -1;
	}
}

void WiFi_ConnectAP(struct net_device *dev, struct wifi_ap_info *ap_info)
{
	struct net_device *net_dev;
	
	net_dev = (struct net_device *)dev_get_by_type(NULL, HLD_DEV_TYPE_WIFI);
	if(net_dev!=dev)
	{
		dev=net_dev;
	}
	if(dev == NULL)
		return ;
	
	if(dev->connect_ap)
	{
		dev->connect_ap(dev, (UINT32)ap_info);
	}
	else
		WiFi_PRINTF("[ERR]%s: WiFi device plug out or not init done\n", __FUNCTION__);
	return;
}

void WiFi_DisConnectAP(struct net_device *dev, struct wifi_ap_info *ap_info)  //cpadd_110421
{
	struct net_device *net_dev;
	
	net_dev = (struct net_device *)dev_get_by_type(NULL, HLD_DEV_TYPE_WIFI);
	if(net_dev!=dev)
	{
		dev=net_dev;
	}

	if(dev == NULL)
		return;
	
	if(dev->disconnect_ap)
	{
		dev->disconnect_ap(dev, (UINT32)ap_info);
	}
	else
		WiFi_PRINTF("[ERR]%s: WiFi device plug out or not init done\n", __FUNCTION__);
	return;
}
int WiFi_GetConnStatus(struct net_device *dev)
{
	struct net_device *net_dev;
	
	net_dev = (struct net_device *)dev_get_by_type(NULL, HLD_DEV_TYPE_WIFI);
	if(net_dev!=dev)
	{
		dev=net_dev;
	}

	if(dev == NULL)
		return -1;
	
	if(dev->get_conn_status)
	{
		return dev->get_conn_status(dev);
	}
	else {
		WiFi_PRINTF("[ERR]%s: WiFi device plug out or not init done\n", __FUNCTION__);
		return -1;
	}
}

int WiFi_GetPlugStatus(struct net_device *dev)
{
	struct net_device *net_dev;
	
	net_dev = (struct net_device *)dev_get_by_type(NULL, HLD_DEV_TYPE_WIFI);
	if(net_dev!=dev)
	{
		dev=net_dev;
	}

	if(dev == NULL)
		return -1;
	
	if(dev->get_plug_status)
	{
		return dev->get_plug_status(dev);
	}
	else {
		//WiFi_PRINTF("[ERR]%s: WiFi device plug out or not init done\n", __FUNCTION__);
		//return -1;
		return WIFI_MEDIA_DISCONNECTED;
	}	
}

int WiFi_SetEnableStatus(struct net_device *dev, int enable)
{
	struct net_device *net_dev;
	
	net_dev = (struct net_device *)dev_get_by_type(NULL, HLD_DEV_TYPE_WIFI);
	if(net_dev!=dev)
	{
		dev=net_dev;
	}

	if(dev == NULL)
		return -1;
	
	if(dev->set_enable_status)
	{
		if(enable) {
			//dev->set_enable_status(dev, TRUE);
			#if 0//cpl_0312 (sample for set mac cstm assigned address)
			WiFi_SetInfo(dev,0,"7c:dd:90:02:3d:11" );  
			#endif
			
			WiFi_SwtichMedia(WIFI_SWITCH_MAC_TO_WIFI);
		}else {
			WiFi_SwtichMedia(WIFI_SWITCH_WIFI_TO_MAC);
			//dev->set_enable_status(dev, FALSE);
		}
	}
	else {
		WiFi_PRINTF("[ERR]%s: WiFi device plug out or not init done\n", __FUNCTION__);
		return -1;
	}
}

int WiFi_GetEnableStatus(struct net_device *dev)
{
	struct net_device *net_dev;
	
	net_dev = (struct net_device *)dev_get_by_type(NULL, HLD_DEV_TYPE_WIFI);
	if(net_dev!=dev)
	{
		dev=net_dev;
	}

	if(dev == NULL)
		return -1;
	
	if(dev->get_enable_status)
	{
		return dev->get_enable_status(dev);
	}
	else {
		WiFi_PRINTF("[ERR]%s: WiFi device plug out or not init done\n", __FUNCTION__);
		return -1;
	}	
}

int WiFi_GetReadyStatus(struct net_device *dev)
{
	struct net_device *net_dev;
	
	net_dev = (struct net_device *)dev_get_by_type(NULL, HLD_DEV_TYPE_WIFI);
	if(net_dev!=dev)
	{
		dev=net_dev;
	}

	if(dev == NULL)
		return -1;
	
	if(dev->get_ready_status)
	{
		return dev->get_ready_status(dev);
	}
	else {
		WiFi_PRINTF("[ERR]%s: WiFi device plug out or not init done\n", __FUNCTION__);
		return -1;
	}	
}

int WiFi_GetAPQuality(struct net_device *dev)
{
	struct net_device *net_dev;
	
	net_dev = (struct net_device *)dev_get_by_type(NULL, HLD_DEV_TYPE_WIFI);
	if(net_dev!=dev)
	{
		dev=net_dev;
	}

	if(dev == NULL)
		return -1;
	
	if(dev->get_ap_quality) //cpadd_110420_add_quality
		return dev->get_ap_quality(dev);
	else {
		WiFi_PRINTF("[ERR]%s: WiFi device plug out or not init done\n", __FUNCTION__);
		return -1;
	}
}






static int WiFi_AttachNetif(struct net_device *net_dev)
{
	if(net_dev == NULL)
		return -1;

    if(net_dev->flags & HLD_DEV_STATS_ATTACHED)
    {
        WiFi_PRINTF("%s already attached!\n", net_dev->name);
        return 0;
    }
    
	if (sys_add_netif(IP_ADDR_ANY, IP_ADDR_ANY, IP_ADDR_ANY))
	{
		WiFi_PRINTF("[ERR] %s: add netif fail...\n", __FUNCTION__);
		return -1;
	}

	IP_LOC_CFG ip_cfg;
	get_local_ip_cfg(&ip_cfg);

#ifdef _INVW_JUICE   
	if(0)
#else
	if(ip_cfg.dhcp_on == 1)
#endif		
	{	
		libnet_dhcp_on();
	}
	else
	{
		libnet_set_ipconfig(&ip_cfg);
	}
    net_dev->flags |= HLD_DEV_STATS_ATTACHED;

	return 0;
}

static int WiFi_DetachNetif(struct net_device *net_dev)
{
	struct netif * cur_netif = NULL;
	
	if(net_dev == NULL)
		return -1;

	net_close(net_dev);
    net_dev->flags &= ~(HLD_DEV_STATS_ATTACHED);
    
	cur_netif = netif_find("en0");  	
	if(cur_netif) {

	IP_LOC_CFG ip_cfg;
	get_local_ip_cfg(&ip_cfg);

#ifdef _INVW_JUICE
	if(0)
#else
	//if(ip_cfg.dhcp_on == 1)
#endif	
	{	
		libnet_dhcp_off();
	}
		
		netif_remove(cur_netif);
		FREE(cur_netif);
		cur_netif = NULL;	
	}else {
		WiFi_PRINTF("[ERR] %s: no netif found...\n", __FUNCTION__);
		return -1;
	}

	return 0;
}

int WiFi_SwtichMedia(UINT8 type)
{
	struct net_device *net_dev=NULL;
	struct netif * cur_netif = NULL; 
	struct netif * m_p_netif_wifi = NULL; 

	osal_mutex_lock(wifi_switch_mutex, OSAL_WAIT_FOREVER_TIME);
	
	switch(type) {
		case WIFI_SWITCH_MAC_TO_WIFI:
			net_dev = dev_get_by_type(NULL, HLD_DEV_TYPE_NET);
			if(net_dev) {
				WiFi_DetachNetif(net_dev);

				#ifdef _INVW_JUICE
				osal_delay(1000);
				#endif
				
				DBG_WIFI_PRINTF(" detach Mac  ------------------>\n");
			}else
				DBG_WIFI_PRINTF(" no need to detach Mac  ------------------>\n");

			net_dev = dev_get_by_type(NULL, HLD_DEV_TYPE_WIFI);
			if(net_dev) {
				if(net_dev->set_enable_status)
					net_dev->set_enable_status(net_dev, TRUE);
				WiFi_AttachNetif(net_dev);

				#ifdef _INVW_JUICE
				osal_delay(1000);
				#endif
				
				DBG_WIFI_PRINTF("<------------------  attach WiFi \n");
			}else
				DBG_WIFI_PRINTF("<------------------  can't attach WiFi \n");
			
			break;
			
		case WIFI_SWITCH_WIFI_TO_MAC:
			net_dev = dev_get_by_type(NULL, HLD_DEV_TYPE_WIFI);
			if(net_dev) {
				WiFi_DetachNetif(net_dev);
				if(net_dev->set_enable_status)
					net_dev->set_enable_status(net_dev, FALSE);

				#ifdef _INVW_JUICE
				osal_delay(1000);
				#endif
				
				DBG_WIFI_PRINTF(" detach WiFi  ------------------>\n");
			}else
				DBG_WIFI_PRINTF(" no need to detach WiFi  ------------------>\n");

			net_dev = dev_get_by_type(NULL, HLD_DEV_TYPE_NET);
			if(net_dev) {
				WiFi_AttachNetif(net_dev);

				#ifdef _INVW_JUICE
				osal_delay(1000);
				#endif
				
				DBG_WIFI_PRINTF("<------------------  attach Mac \n");
			}else
				DBG_WIFI_PRINTF("<------------------  no need to attach Mac \n");
			
			break;
			
		default:
			WiFi_PRINTF("[ERR] %s: wrong swtich type...\n", __FUNCTION__);
			break;
	}

	osal_mutex_unlock(wifi_switch_mutex);

	return 0;
}

RET_CODE WiFi_detach(struct net_device *dev)
{
	WiFi_PRINTF("rt5370_detach: Detach net device driver.\n");

	#ifdef DUAL_ENABLE
	dev_en_remote(0);
	#endif
	dev_free(dev);
	#ifdef DUAL_ENABLE
	dev_en_remote(1);
	#endif
	dev = NULL ;

	osal_mutex_delete(wifi_switch_mutex);
	
	return RET_SUCCESS;
}

RET_CODE WiFi_attach(void)
{
	struct net_device *dev;

	//alloc memory space for net device.
	dev = dev_alloc(wifi_dev_name, HLD_DEV_TYPE_WIFI, sizeof(struct net_device));
	if (dev == NULL)
	{
		WiFi_PRINTF("[ERR] %s: alloc net device error!\n", __FUNCTION__);
		return ERR_NO_MEM;
	}

	
	dev->next = NULL;

	//init function point (some functions assigned latter)
	dev->attach = WiFi_attach;
	dev->detach = WiFi_detach;
	dev->open = NULL;
	dev->close = NULL;
	dev->ioctl = NULL;
	dev->send_packet = NULL;
	dev->callback = NULL;
	dev->get_info = NULL;
	dev->set_info = NULL;
	dev->scan_ap = NULL;
	dev->get_ap_list = NULL;
	dev->connect_ap = NULL;
	dev->get_conn_status = NULL;

	/* Add this device to queue */ 
	#ifdef DUAL_ENABLE
	dev_en_remote(0);
	#endif
	if (dev_register(dev) != RET_SUCCESS)
	{
		WiFi_PRINTF("Register net device error!\n");
		dev_free(dev);
		#ifdef DUAL_ENABLE
		dev_en_remote(1);
		#endif
		return RET_FAILURE;
	}
	#ifdef DUAL_ENABLE
	dev_en_remote(1);
	#endif
	wifi_switch_mutex = osal_mutex_create();
	WiFi_PRINTF("Register WiFi net dev:%08x (priv: %08x)\n", dev, dev->priv);
	
}
#else

int WiFi_SwtichMedia(UINT8 type)
{
	return RET_FAILURE;
}
#endif

INT32 rt5370_init(UINT32  pNode);
int WiFi_DeviceEnable(BOOL bEnable)
{

	struct net_device *dev;

	//alloc memory space for net device.
	dev = (struct net_device *)dev_get_by_type(NULL, HLD_DEV_TYPE_WIFI);
	if (dev == NULL)
	{
		//WiFi_PRINTF("[ERR] %s: alloc net device error!\n", __FUNCTION__);
		return ERR_NO_MEM;
	} 


	#ifdef WIFI_SUPPORT
	dev->rt5370_callback=rt5370_init;
	#else
	dev->rt5370_callback=NULL;
	#endif

	dev->wifi_device_enable = bEnable;


	
}


int WiFi_SetDevicePidVid(UINT16 pid,UINT16 vid)
{

	struct net_device *dev;
	int cnt=0;
	//alloc memory space for net device.
	dev = (struct net_device *)dev_get_by_type(NULL, HLD_DEV_TYPE_WIFI);
	if (dev == NULL)
	{
		//WiFi_PRINTF("[ERR] %s: alloc net device error!\n", __FUNCTION__);
		return ERR_NO_MEM;
	} 

	if(cnt>10)
	{
		libc_printf("only support max 10 customer device pid vid\n");
		return FALSE;
	}

	for(cnt=0; cnt<10 ; cnt++)
	{
		if(dev->customer_vid[cnt]==0)  
			break;
	}

 	dev->customer_vid[cnt]=vid;
	dev->customer_pid[cnt]=pid;
	
	libc_printf("cust = %d ; vid = 0x%x ; pid = 0x%x \n",cnt,dev->customer_vid[cnt],dev->customer_pid[cnt]);

	return TRUE;

}







